#!/bin/dash

#Copyright (c) 2014 Luigi Tarenga <luigi.tarenga@gmail.com>
#Distributed under the terms of a MIT license.

#request-vote:
#input  term candidate_id last_log_index last_log_term
#output term vote_granted

#todo check if present in configuration for che configuration change

read election_timeout < temp/election_timeout
election_timeout=$((election_timeout-1))
echo $election_timeout > temp/election_timeout

read current_term   < state/current_term
read voted_for      < state/voted_for
read last_log_index < state/last_log_index
read hostname       < temp/hostname
read commit_index   < temp/commit_index
read cluster_nodes  < conf/cluster_nodes

if [ -d state-machine-log/"$last_log_index" ] ; then
   last_log_term=$(echo state-machine-log/"$last_log_index"/*)
   last_log_term=${last_log_term##state-machine-log/*/}
else
   read last_log_term < state-machine-snapshot/cur/last_included_term
fi

cluster_nodes_new=""
cluster_nodes_old_new="$cluster_nodes"
if [ -f conf/cluster_nodes_new ] ; then
   read cluster_nodes_new < conf/cluster_nodes_new
   cluster_nodes_old_new=$(for h in $cluster_nodes $cluster_nodes_new ; do echo $h ; done | sort -u)
fi

if [ "$election_timeout" -le 0 ] ; then
   #restart election
   cp conf/election_timeout temp/election_timeout
   echo "" > state/voted_for
   current_term=$((current_term+1))
   echo $current_term > state/current_term
   echo $(date) restarting election.
fi

if [ -z "$voted_for" ] ; then
   voted_for="$hostname"
   echo "$voted_for" > state/voted_for
fi

if [ "$voted_for" = "$hostname" ] ; then
   votes=1

   for h in $cluster_nodes_old_new ; do
      [ "$h" = "$hostname" ] && continue
      votes=$((votes+1))
      ssh $sshopt $h "cd $PWD ; lock_timeout=4 exec internals/activity-lock-ex internals/request-vote " \
                     "$current_term $hostname $last_log_index $last_log_term" > temp/${h}_vote < /dev/null &
   done

   wait

   winner=true
   for configuration in "$cluster_nodes" "$cluster_nodes_new" ; do
      i=1; votes=1
      for h in $configuration ; do
         [ "$h" = "$hostname" ] && continue
         read term vote_granted < temp/${h}_vote
         if [ "$((term+0))" -eq "$current_term" -a "$vote_granted" = "true" ] ; then
            votes=$((votes+1))
         elif [ "$((term+0))" -gt "$current_term" -a "$vote_granted" = "false" ] ; then
            #found a higher term, turn to follower
            cp conf/election_timeout temp/election_timeout
            echo ""            > state/voted_for
            echo follower      > temp/server_role
            echo "$((term+0))" > state/current_term
            echo $(date) $h sent a higher term. switching to follower.
            exit 0
         fi
         i=$((i+1))
      done
      majority=$((i/2+1))
      [ "$votes" -lt "$majority" ] && winner=false
   done

   if [ "$winner" = "true" ] ; then
      rm -f temp/*_match_index
      rm -f temp/*_next_index
      for h in $cluster_nodes_old_new ; do
         [ "$h" = "$hostname" ] && continue
         echo $((last_log_index+1)) > temp/${h}_next_index
         echo 0                      > temp/${h}_match_index
         echo 1                      > temp/${h}_follower_heartbeat
      done
      echo leader    > temp/server_role
      echo $hostname > temp/current_leader
      internals/append-entry $current_term $hostname $last_log_index $last_log_term \
         $current_term noop "" $commit_index > temp/${hostname}_append_result
      echo $(date) switching to leader.
   fi
else
   #already voted for another one.... turn to follower
   echo follower > temp/server_role
   echo $(date) already voted for another host. switching to follower.
fi
